Summary

data <- read.csv('./mp_batteries.csv')
prettyTable(data)

Sprawdźmy czy potrzebne będzie usunięcie wartości pustych.

for (name in colnames(data)){
  isMissing <- sum(is.na(data[[name]]))
  if (isMissing == 0) {
    print(paste('Brak wartości pustych dla', name))
  } else {
     print(paste(name, 'posiada :', isMissing, 'wartości puste', sep=' '))
  }
}
## [1] "Brak wartości pustych dla Battery.ID"
## [1] "Brak wartości pustych dla Battery.Formula"
## [1] "Brak wartości pustych dla Working.Ion"
## [1] "Brak wartości pustych dla Formula.Charge"
## [1] "Brak wartości pustych dla Formula.Discharge"
## [1] "Brak wartości pustych dla Max.Delta.Volume"
## [1] "Brak wartości pustych dla Average.Voltage"
## [1] "Brak wartości pustych dla Gravimetric.Capacity"
## [1] "Brak wartości pustych dla Volumetric.Capacity"
## [1] "Brak wartości pustych dla Gravimetric.Energy"
## [1] "Brak wartości pustych dla Volumetric.Energy"
## [1] "Brak wartości pustych dla Atomic.Fraction.Charge"
## [1] "Brak wartości pustych dla Atomic.Fraction.Discharge"
## [1] "Brak wartości pustych dla Stability.Charge"
## [1] "Brak wartości pustych dla Stability.Discharge"
## [1] "Brak wartości pustych dla Steps"
## [1] "Brak wartości pustych dla Max.Voltage.Step"

Nasz zestaw nie zawiera wartości pustych.

Zbiór danych składa się z 4351 rekordów. Każdy rekord posiada 12 wartości numerycznych, 4 tekstowe i id. Podsumujemy wartości nienumeryczne. Jedyna wartość, która nas będzie interesować to ilość unikatowych wartości.

textSummary <- data %>% 
  select(Battery.Formula:Formula.Discharge) %>%
  summarise(across(everything(), n_distinct, .names = 'unique {.col}'))
textSummary %>% knitr::kable()
unique Battery.Formula unique Working.Ion unique Formula.Charge unique Formula.Discharge
3301 10 2096 3173

Jedyna przydatna dla nas kolumna to Working.Ion.

Podsumujmy teraz wartości numeryczne.

summaryDf <- data.frame(
  mean = numeric(),
  median = numeric(),
  min = numeric(),
  max = numeric(),
  sd = numeric()
)
colNames <- data %>%
  select(Max.Delta.Volume:Max.Voltage.Step) %>% colnames()
for (col in colNames) {
  column <- data[col]
  res <- column %>% summarise(
    across(everything(), 
           c(mean, median, min, max, sd)
     )
    )
  colnames(res) <- c('mean', 'median', 'min', 'max', 'sd')
  summaryDf <- rbind(summaryDf, res)
}
rownames(summaryDf) <- colNames
summaryDf %>% knitr::kable()
mean median min max sd
Max.Delta.Volume 0.3753137 0.0420271 0.0000162 2.931932e+02 6.8518375
Average.Voltage 3.0831427 3.3005818 -7.7547512 5.456883e+01 1.8220562
Gravimetric.Capacity 158.2908894 130.6909797 5.1765430 2.557627e+03 164.9136411
Volumetric.Capacity 610.6240987 507.0312049 24.0790699 7.619191e+03 563.8531258
Gravimetric.Energy 444.1063802 401.7876573 -583.5458444 5.926950e+03 351.0481297
Volumetric.Energy 1664.0484137 1463.7877150 -2208.0745659 1.830590e+04 1297.7985678
Atomic.Fraction.Charge 0.0398558 0.0000000 0.0000000 9.090909e-01 0.0885604
Atomic.Fraction.Discharge 0.1590772 0.1428571 0.0074074 9.933333e-01 0.1203743
Stability.Charge 0.1425666 0.0731920 0.0000000 6.487098e+00 0.3782776
Stability.Discharge 0.1220717 0.0487845 0.0000000 6.277809e+00 0.3523182
Steps 1.1670880 1.0000000 1.0000000 6.000000e+00 0.4637496
Max.Voltage.Step 0.1502897 0.0000000 0.0000000 2.696069e+01 0.6300680

Przyjrzyjmy się dokładniej rozkładom wartości numerycznym. Wszystkie przypominają rozkład normalny, który jest silnie skoncentrowany wokół mediany. Większość rozkładów posiada kilka wartości skrajnych. Working.Ion ma dominującą wartość - Li.

excludeHist <- c("Battery.ID","Battery.Formula", "Formula.Charge", "Formula.Discharge", "Working.Ion" ,"Max.Delta.Volume")

histData <- data[ , !(names(data) %in% excludeHist)]
colNames <- histData %>% colnames()

# 

for (column in colNames) {
  minVal <- summaryDf[column, 'min']
  maxVal <- summaryDf[column, 'max']
  bins <- (maxVal - minVal) / 100
  graph <- histData %>% ggplot(aes_string(x = column)) + 
    geom_histogram(fill = 'blue', binwidth = bins) +
    labs(title = paste(column, ' histogram'), x = column, y = 'Frequency') +
    theme_minimal()
  plot(graph)
}
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

graph <- data %>% ggplot(aes(x = Max.Delta.Volume)) +
  geom_histogram(binwidth = 0.01, fill = 'blue', ) +
  labs(title = 'Histogram of Max.Delta.Volume', x = 'Max.Delta.Volume', y = 'Frequency') +
  xlim(-0.1, 1.5) + theme_minimal()
plot(graph)
## Warning: Removed 65 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 2 rows containing missing values or values outside the scale range
## (`geom_bar()`).

graph <- data.frame(table(data$Working.Ion)) %>% ggplot(aes(x = Var1, y = Freq)) + 
    geom_col(fill = 'blue') +
    labs(title = 'Working Ion histogram', y = 'Frequency', x ='Ion') +
    theme_minimal()
plot(graph)

Przyjrzyjmy się korelacjom między wartościami. Występuje kilka korelacji wartych dalszej analizy - Charge/Discharge pairs, energy, capacity.

cor_matrix <- cor(data %>% select_if(is.numeric), method = "pearson")
cor_data <- melt(cor_matrix)
graph <- cor_data %>%
  ggplot(aes(x = Var1, y = Var2, fill = value)) +
  geom_tile() +
  scale_fill_gradient2(
    low = "blue",
    mid = "white",
    high = "red",
    midpoint = 0,
    limits = c(-1, 1)
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) +
  coord_fixed() +
  labs(title = "Correlation Matrix", x = "Fields", y = "Fields")

#ggplotly(corr)
plot(graph)

Zależność pomiędzy Average Voltage i Gravimetric Energy jest gorzej widoczna przez obecność wartości skrajnych na wykresie.

## [1] "Korelacja dla Average.Voltage i Gravimetric.Energy 0.665652274348073"
## [1] "Korelacja dla Atomic.Fraction.Discharge i Gravimetric.Capacity 0.680771641177293"
## [1] "Korelacja dla Atomic.Fraction.Discharge i Volumetric.Capacity 0.618018604601293"
data %>% ggplot(aes(x = Average.Voltage, y=Gravimetric.Energy)) +
  geom_point(color = "blue") +  # Scatter points
  geom_smooth(method = "lm") +  # Regression line
  labs(title = "Average Voltage and Gravimetric Energy",
       x = "Avg. Voltage",
       y = "Gravimetric Energy") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

data %>% 
  filter(Average.Voltage < 30) %>% 
  ggplot(aes(x = Average.Voltage, y=Gravimetric.Energy)) +
  geom_point(color = "blue") +  # Scatter points
  geom_smooth(method = "lm") +  # Regression line
  labs(title = "Average Voltage and Gravimetric Energy without outliers",
       x = "Avg. Voltage",
       y = "Gravimetric Energy") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

data %>% ggplot(aes(x = Atomic.Fraction.Discharge, y=Gravimetric.Capacity)) +
  geom_point(color = "blue") +  # Scatter points
  geom_smooth() +  # Regression line
  geom_smooth(method = "lm") +  # Regression line
  labs(title = "Atomic Fraction Discharge and Gravimetric Capacity",
       x = "Atomic Fraction Discharge",
       y = "Gravimetric Capacity") +
  theme_minimal()
## `geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'
## `geom_smooth()` using formula = 'y ~ x'

data %>% ggplot(aes(x = Atomic.Fraction.Discharge, y=Volumetric.Capacity)) +
  geom_point(color = "blue") +  # Scatter points
  geom_smooth(method = "lm") +  # Regression line
  labs(title = "Atomic Fraction Discharge and Volumetric Capacity",
       x = "Atomic Fraction Discharge",
       y = "Volumetric Capacity") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

colNames <- data %>% colnames()
for (col in colNames) {
  #print(cor.test(data[col], data$Working.Ion))
}